/*
 * @Author: 李石
 * @Date: 2024-05-20 16:54:04
 * @LastEditors: lishi
 * @LastEditTime: 2024-06-28 10:50:29
 * @Description: 
 * Copyright (c) 2024 by ${lishi0105@163.com}, All Rights Reserved. 
 */
#include "logger.hpp"
#include "FrameData.hpp"
#include "ZLMFramePuller.hpp"

static void API_CALL on_track_frame_out(void* user_data, mk_frame frame) 
{
    ZLMFramePuller* instance = static_cast<ZLMFramePuller*>(user_data);
    if (instance) {
        instance->onGetMkFrame(frame);
    }
}

static void API_CALL on_mk_play_event_func(void* user_data, int err_code, const char* err_msg, mk_track tracks[],
    int track_count) 
{
    (void)err_msg;
    if (err_code == 0) {
        int i = 0;
        for (i = 0; i < track_count; ++i) {
            if (mk_track_is_video(tracks[i])) {
                try{
                    ZLMFramePuller* instance = static_cast<ZLMFramePuller*>(user_data);
                    LOGI("got video track:{} {} fps :{} ", mk_track_codec_id(tracks[i]), mk_track_codec_name(tracks[i]), mk_track_video_fps(tracks[i]));
                    if (instance) {
                        instance->onVideoAttribute(mk_track_codec_id(tracks[i]),
                                                    mk_track_video_fps(tracks[i]),
                                                    mk_track_video_width(tracks[i]), 
                                                    mk_track_video_height(tracks[i]));                    
                    }
                }catch(std::exception &e){
                    std::cout << e.what() << std::endl;
                }
                mk_track_add_delegate(tracks[i], on_track_frame_out, user_data);
            }
        }
    }
}

static void API_CALL on_mk_shutdown_func(void* user_data, int err_code, const char* err_msg, mk_track tracks[], int track_count) 
{
    (void)tracks;
    (void)track_count;
    ZLMFramePuller* instance = static_cast<ZLMFramePuller*>(user_data);
    if(instance){
        instance->resetPullerCounter();
        
    }
    LOGW("play interrupted: {} {}", err_code, err_msg);
}

ZLMFramePuller::ZLMFramePuller(const std::string &url, int rtp_type, pullerDataCallback cb_)
    : FramePuller(url, rtp_type, cb_),
    configFrames(nullptr),
    configFramesSize(0),
    m_input_url(url),
    m_puller_exit(false),
    player_(nullptr),
    m_recv_count(0)
{
    m_pull_thread = std::thread(&ZLMFramePuller::FramePullThread, this);
}

ZLMFramePuller::~ZLMFramePuller()
{
    m_puller_exit = true;
    if (m_pull_thread.joinable()) {
        m_pull_thread.join();
    }
    if (player_) {
        mk_player_release(player_);
        player_ = NULL;
    }
    LOGI("ZLMFramePuller::~ZLMFramePuller");
}

void ZLMFramePuller::clearConfigFrames()
{
    if (configFrames != nullptr) {
        free(configFrames);
        configFrames = nullptr;
    }
    configFramesSize = 0;
}

void ZLMFramePuller::FramePullThread()
{
    m_puller_exit = false;
    while(!m_puller_exit)
    {
        player_ = mk_player_create();
        mk_player_set_on_result(player_, on_mk_play_event_func, this);
        mk_player_set_on_shutdown(player_, on_mk_shutdown_func, this);
        char rtp_type[2] = {0};
        snprintf(rtp_type, sizeof(rtp_type), "%d", m_rtp_type);
        mk_player_set_option(player_, "rtp_type", rtp_type);
        mk_player_set_option(player_, "protocol_timeout_ms", "5000");
        mk_player_play(player_, m_input_url.c_str());
        m_recv_count = 0;
        int32_t m_recv_count_save = 0;
        while(!m_puller_exit){
            std::this_thread::sleep_for(std::chrono::milliseconds(3000));
            if(m_recv_count_save == m_recv_count){
                LOGE("recv no data");
                break;
            }
            m_recv_count_save = m_recv_count;
        }
        if (player_) {
            mk_player_release(player_);
            player_ = nullptr;
        }
        std::this_thread::sleep_for(std::chrono::milliseconds(1000));
    }
    if (player_) {
        mk_player_release(player_);
        player_ = nullptr;
    }
    mk_stop_all_server();
    LOGD("ZLMFramePuller::FramePullThread exit");
}

void ZLMFramePuller::onGetMkFrame(const mk_frame frame_)
{
    m_recv_count ++;
    if(m_recv_count >= 65535){
        m_recv_count = 1;
    }
    if (frame_ == nullptr || m_puller_exit) {
		return;
	}
    auto frame = FrameData::CreateShared(frame_);
    if (frame.get() == nullptr || frame->data() == nullptr || frame->size() == 0) {
        return;
    }
    auto data = frame->data();
    auto size = frame->size();
    // LOGI(frame->toString());
    if (frame->configFrame()) {
        size_t newSize = configFramesSize + size;
        unsigned char* newConfigFrames = (unsigned char*)realloc(configFrames, newSize);
        if (newConfigFrames == nullptr) {
            std::cout << "realloc failed" << std::endl;
            clearConfigFrames();
            return ;
        }
        configFrames = newConfigFrames;
        memcpy(configFrames + configFramesSize, data, size);
        configFramesSize = newSize;
        return;
    }
    if (configFrames != nullptr && configFramesSize != 0) {
        if (frame->dropAble()) {
            size_t newSize = configFramesSize + size;
            unsigned char* newConfigFrames = (unsigned char*)realloc(configFrames, newSize);
            if (newConfigFrames == nullptr) {
                std::cout << "realloc failed" << std::endl;
                clearConfigFrames();
                return ;
            }
            configFrames = newConfigFrames;
            memcpy(configFrames + configFramesSize, data, size);
            configFramesSize = newSize;
        }else{
            size_t totalSize = configFramesSize + size;
            if(totalSize == 0 || totalSize > 1024 * 1024 * 10){
                LOGE("totalSize is 0");
                clearConfigFrames();
                return ;
            }
            uint8_t* mergedData = new uint8_t[totalSize + 1];
            if (mergedData == nullptr) {
                std::cout << "malloc failed" << std::endl;
                clearConfigFrames();
                return ;
            }
            memcpy(mergedData, configFrames, configFramesSize);
            memcpy(mergedData + configFramesSize, data, size);
            clearConfigFrames();
            auto new_frame = FrameData::CreateShared(mergedData, totalSize, frame_);
            onGetFrameData(new_frame);
            delete[] mergedData;
            mergedData = nullptr;
        }
        return ;
    }
    onGetFrameData(frame);
}